package utils;

import com.alibaba.fastjson.JSONObject;
import model.command.Command;
import model.command.CommandPos;
import model.command.RmCommand;
import model.command.SetCommand;

import java.io.File;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RotateUtils {

    private static final String RW_MODE = "rw";
    private static final String TABLE = ".table";
    private static final ReentrantReadWriteLock indexLock=new ReentrantReadWriteLock();
    /*存储data.table文件中的索引*/
    public static TreeMap<Integer, HashMap<String, CommandPos>> tableIndex;
    private static final long MAX_SIZE = 2 * 1024;
//    初始化表索引和读写锁
    public RotateUtils() {
        this.tableIndex = new TreeMap<>();

    }

    public void rotateTable(String dataDir, String filePath, HashMap<String, CommandPos> index, int length) throws Exception {
        try {
            RandomAccessFile file = new RandomAccessFile(dataDir + "data" + length + TABLE, RW_MODE);
                ArrayList<String> keys = new ArrayList<>(index.keySet());  // 获取索引中的所有键
                int start = 4;  // 初始化写入位置
                for (String key : keys) {
                    CommandPos cmdPos = index.get(key);
                    byte[] bytes = RandomAccessFileUtil.readByIndex(filePath, cmdPos.getPos(), cmdPos.getLen());  // 从旧文件中读取命令字节数组
                    file.writeInt(cmdPos.getLen());
                    file.write(bytes);
                    index.get(key).setPos(start);  // 更新索引中该键的写入位置
                    start += (4 + cmdPos.getLen());  // 更新写入位置
                }
                tableIndex.put(length, index);  // 将更新后的索引存储到tableIndex中
                if (length > 1) {  // 若table文件个数大于1，可rotate后压缩
                    this.CompressFile(length, dataDir);  // 压缩文件
                }
                file.close();

        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    public void CompressFile(int length, String dataDir) throws Exception{
        new Thread(() -> {
            try {
                if (length >= 2) {
                    int newIndex = 1;//data1,2合并成一个table文件
                    indexLock.writeLock().lock();
                    System.out.println("文件正在压缩中.....");
                    //合并文件
                    for (int tableindex= 1; tableindex <= tableIndex.size(); tableindex += 2) {
                        File deleteFile1 = new File(dataDir + "data" + tableindex + TABLE);
                        File deleteFile2 = new File(dataDir + "data" + (tableindex + 1) + TABLE);
                        if (!deleteFile1.exists() || !deleteFile2.exists()) {
                            deleteFile1.renameTo(new File(dataDir + "data" + newIndex + TABLE));
                            break;
                        }
                        //旧表
                        RandomAccessFile file1 = new RandomAccessFile(dataDir + "data" + tableindex + TABLE, RW_MODE);
                        //新表
                        RandomAccessFile file2 = new RandomAccessFile(dataDir + "data" + (tableindex + 1) + TABLE, RW_MODE);
                        CompressionUtils fileZipper = new CompressionUtils(file1, file2);
                        fileZipper.CompressFilesProcess(dataDir,tableIndex,tableindex,newIndex);
                        newIndex++;
                    }
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }).start();

    }

    //    查询的时候如果两个内存表和data.table都没有查询到的话就去rotate的table里面
    public static String findRotateTable(String key, String fileDir) {
        String result = null;
        for (int i = tableIndex.size(); i > 0; i--) {
            HashMap<String, CommandPos> index = tableIndex.get(i);
            indexLock.readLock().lock();
            // 从索引中获取信息
            CommandPos cmdPos = index.get(key);
            if (cmdPos == null) {
                indexLock.readLock().unlock();
                continue;
            }
            byte[] commandBytes = RandomAccessFileUtil.readByIndex(fileDir + "data" + i + TABLE, cmdPos.getPos(), cmdPos.getLen());

            JSONObject value = JSONObject.parseObject(new String(commandBytes));
            Command cmd = CommandUtil.jsonToCommand(value);
            if (cmd instanceof SetCommand) {
                indexLock.readLock().unlock();
                result = ((SetCommand) cmd).getValue();
                break;
            }
            if (cmd instanceof RmCommand) {
                indexLock.readLock().unlock();
                break;
            }
        }
        return result;
    }

}
